﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using A5Lib;
using System.Xml.XPath;
using System.Xml;
using System.IO;
using System.Web.Hosting;

namespace A5Services
{

    /// <summary>
    /// 
    /// </summary>
    public static class XMLDataManager
    {
        //private static string XMLFile = @"E:\1 ASU\CSE445Distributed\A5\A5Project\A5Services\DataStore.xml";
        //private static string XMLFile = @"C:\Users\james\Desktop\A5\A5Services\DataStore.xml";
        private static string XMLFile = HostingEnvironment.ApplicationPhysicalPath + "DataStore.xml";

        /// <summary>
        /// Returns the User object given the username.
        /// </summary>
        /// <param name="username">The username to retrieve.</param>
        /// <returns>Returns null if the username does not exist. Otherwise returns the User object populated with all user information.</returns>
        public static User GetUser(string username)
        {
            lock (XMLFile)
            {
                User u = new User();
                u.Username = username;


                XPathDocument xpathDoc;
                XPathNavigator xmlNav;
                XPathNodeIterator xmlNI;

                //open xml doc
                xpathDoc = new XPathDocument(XMLFile);
                xmlNav = xpathDoc.CreateNavigator();

                //select on the given username
                xmlNI = xmlNav.Select("/UserData/User[@username='" + username + "']");
                if (xmlNI.Count != 1) return null;

                //get password
                xmlNI.MoveNext();
                u.Password = xmlNI.Current.GetAttribute("password", "");

                //iterate over appliances
                xmlNI = xmlNI.Current.SelectChildren("Appliance", "");
                Console.WriteLine(DateTime.Now.ToString());
                while (xmlNI.MoveNext())
                {
                    //get appliance name
                    string name = xmlNI.Current.GetAttribute("name", "");
                    Appliance a = new Appliance(name);

                    //get appliance schedule
                    XPathNodeIterator sched = xmlNI.Current.Select("Schedule/DateTimePair");
                    while (sched.MoveNext())
                    {
                        DateTime d1, d2;
                        XPathNodeIterator tmp = sched.Current.SelectChildren("StartTime", "");
                        tmp.MoveNext();
                        d1 = Convert.ToDateTime(tmp.Current.Value);
                        tmp = sched.Current.SelectChildren("EndTime", "");
                        tmp.MoveNext();
                        d2 = Convert.ToDateTime(tmp.Current.Value);


                        DateTimePair dtp = DateTimePair.CreateDateTimePair(d1, d2);
                        if (dtp != null)
                        {
                            a.ScheduleTimes.addDateTimePair(d1, d2);
                        }
                    }
                    u.addAppliance(a);

                }
                return u;
            }
        }

        /// <summary>
        /// Writes a user to the xml data store.
        /// </summary>
        /// <param name="u">The user to write.</param>
        /// <param name="newUser">Specifies whether or not this is a new user(true) or an existing user(false).</param>
        /// <returns>True if succesfully written user. Returns false if newUser=true and the username already exists.
        /// Returns false if newUser=false and username does not exist.</returns>
        public static bool WriteUser(User u, bool newUser)
        {
            lock (XMLFile)
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(XMLFile);
                XmlElement usernode = (XmlElement)doc.SelectSingleNode("/UserData/User[@username='" + u.Username + "']");
                
                //make sure if new user that the username does not exist.
                if (usernode != null && newUser) return false;

                //make sure if not new user that the user actually exists.
                if (usernode == null && !newUser) return false;

                //new user, append to UserData node.
                if (newUser)
                {
                    XmlElement userdatanode = (XmlElement)doc.SelectSingleNode("/UserData");
                    usernode = doc.CreateElement("User");
                    userdatanode.AppendChild(usernode);
                }

                usernode.RemoveAll();
                usernode.SetAttribute("username", u.Username);
                usernode.SetAttribute("password", u.Password);

                foreach (Appliance a in u.Appliances)
                {
                    XmlElement appliancetmp = doc.CreateElement("Appliance");
                    appliancetmp.SetAttribute("name", a.Name);
                    XmlElement sched = doc.CreateElement("Schedule");
                    appliancetmp.AppendChild(sched);
                    foreach (DateTimePair dtp in a.ScheduleTimes.Dates)
                    {
                        XmlElement dtptmp = doc.CreateElement("DateTimePair");
                        XmlElement start = doc.CreateElement("StartTime");
                        start.InnerText = dtp.d1.ToString();
                        XmlElement end = doc.CreateElement("EndTime");
                        end.InnerText = dtp.d2.ToString();
                        dtptmp.AppendChild(start);
                        dtptmp.AppendChild(end);

                        sched.AppendChild(dtptmp);
                    }

                    usernode.AppendChild(appliancetmp);
                }

                doc.Save(XMLFile);
            }
            return true;

        }

        /// <summary>
        /// Validates a given username and password.
        /// </summary>
        /// <param name="username">The username to validate.</param>
        /// <param name="password">The password to validate.</param>
        /// <returns>True if successful.</returns>
        public static bool ValidateUser(string username, string password)
        {
            XPathDocument xpathDoc;
            XPathNavigator xmlNav;
            XPathNodeIterator xmlNI;

            //open xml doc
            xpathDoc = new XPathDocument(XMLFile);
            xmlNav = xpathDoc.CreateNavigator();

            //select on the given username
            xmlNI = xmlNav.Select("/UserData/User[@username='" + username + "']");
            //false if no user with given username
            if (xmlNI.Count != 1) return false;

            //check password
            xmlNI.MoveNext();
            if (xmlNI.Current.GetAttribute("password", "") != password) return false;

            return true;
        }

        /// <summary>
        /// Checks wether or not a given username is already used.
        /// </summary>
        /// <param name="username">The username to check</param>
        /// <returns>True if the username already exists.</returns>
        public static bool CheckUsernameExists(string username)
        {
            XPathDocument xpathDoc;
            XPathNavigator xmlNav;
            XPathNodeIterator xmlNI;

            //open xml doc
            xpathDoc = new XPathDocument(XMLFile);
            xmlNav = xpathDoc.CreateNavigator();

            //select on the given username
            xmlNI = xmlNav.Select("/UserData/User[@username='" + username + "']");
            //false if no user with given username
            if (xmlNI.Count >= 1) return true;

            return false;
        }

        /// <summary>
        /// Changes the specified User's username.
        /// </summary>
        /// <param name="u">The user to change username of.</param>
        /// <param name="newUsername">The new username.</param>
        /// <returns>If newUsername is already taken, returns null, otherwise the User object with the updated Username is returned</returns>
        public static User ChangeUsername(User u, string newUsername)
        {
            lock (XMLFile)
            {
                //The username must not be taken.
                if(CheckUsernameExists(newUsername)) return null;


                XmlDocument doc = new XmlDocument();
                doc.Load(XMLFile);
                XmlElement usernode = (XmlElement)doc.SelectSingleNode("/UserData/User[@username='" + u.Username + "']");
                if (usernode == null)
                {
                    usernode = doc.CreateElement("User");
                }

                usernode.SetAttribute("username", newUsername);
                usernode.SetAttribute("password", u.Password);

                doc.Save(XMLFile);
            }
            u.Username = newUsername;
            return u;
        }

        public static List<string> GetAllUsernames()
        {
            List<string> res = new List<string>();
            XmlDocument doc = new XmlDocument();
            doc.Load(XMLFile);
            XmlNodeList usernodes = doc.SelectNodes("/UserData/User");

            foreach (XmlNode xmln in usernodes)
            {
                res.Add(xmln.Attributes["username"].Value);
            }
            return res;
        }

        public static bool DeleteUser(string username)
        {
            lock (XMLFile)
            {
                //The username must exist
                if (!CheckUsernameExists(username)) return false;

                XmlDocument doc = new XmlDocument();
                doc.Load(XMLFile);
                XmlElement usernode = (XmlElement)doc.SelectSingleNode("/UserData/User[@username='" + username + "']");
                usernode.ParentNode.RemoveChild(usernode);

                doc.Save(XMLFile);
            }
            return true;
        }

    }
}